package com.arashivision.airlive;

import android.Manifest;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.os.Environment;
import android.support.annotation.NonNull;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.view.Surface;
import android.view.View;
import android.widget.Button;
import android.widget.CompoundButton;
import android.widget.EditText;
import android.widget.Switch;
import android.widget.Toast;

import com.arashivision.android.gpuimage.GPUImageSourceOverBlendFilter;
import com.arashivision.arplayer.GlTarget;
import com.arashivision.extradata.ARObject;
import com.arashivision.extradata.protobuf.ExtraMetadata;
import com.arashivision.insta360.arutils.exception.SourceException;
import com.arashivision.insta360.arutils.source.ISource;
import com.arashivision.insta360.arutils.source.SourceFactory;
import com.arashivision.insta360.arutils.utils.CropRect;
import com.arashivision.insta360.export.exporter.ARComposerConfig;
import com.arashivision.insta360.export.services.ExportManager;
import com.arashivision.insta360.export.services.OnExportListener;
import com.arashivision.insta360.export.services.Request;
import com.arashivision.insta360.sdk.render.controller.GestureController;
import com.arashivision.insta360.sdk.render.controller.gyro.CImageGyroController;
import com.arashivision.insta360.sdk.render.controller.gyro.GyroMatrixProvider;
import com.arashivision.insta360.sdk.render.controller.gyro.GyroMatrixType;
import com.arashivision.insta360.sdk.render.controller.gyro.VideoAntiShakeController;
import com.arashivision.insta360.sdk.render.player.IPlayerFactory;
import com.arashivision.insta360.sdk.render.player.PlayerCallback;
import com.arashivision.insta360.sdk.render.renderer.Insta360PanoRenderer;
import com.arashivision.insta360.sdk.render.renderer.model.PlanarRenderModel;
import com.arashivision.insta360.sdk.render.renderer.model.SphericalRenderModel;
import com.arashivision.insta360.sdk.render.renderer.model.SphericalStitchModel;
import com.arashivision.insta360.sdk.render.renderer.model.sticker.ISticker;
import com.arashivision.insta360.sdk.render.renderer.model.sticker.StickerFactory;
import com.arashivision.insta360.sdk.render.renderer.model.sticker.StickerInfo;
import com.arashivision.insta360.sdk.render.renderer.strategy.FishEyeStrategy;
import com.arashivision.insta360.sdk.render.source.OnLoadSourceListener;
import com.arashivision.insta360.sdk.render.util.CoverParametersConverter;
import com.arashivision.insta360.sdk.render.view.PanoramaView;
import com.arashivision.nativeutils.Log;
import com.arashivision.onecamera.AudioSource;
import com.arashivision.onecamera.OneDriverInfo;
import com.arashivision.onecamera.Options;
import com.arashivision.onecamera.RecordType;
import com.arashivision.onecamera.StartCaptureWithoutStorage;
import com.arashivision.onecamera.camerarequest.TakePicture;
import com.arashivision.onecamera.render.RenderMethod;
import com.arashivision.onecamera.render.RenderMode;

import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;
import org.rajawali3d.materials.textures.ISurfacePlayer;
import org.rajawali3d.math.Matrix4;
import org.rajawali3d.math.Quaternion;
import org.rajawali3d.math.vector.Vector3;

import java.io.File;
import java.net.URLDecoder;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Locale;

public class LiveActivity extends AppCompatActivity implements View.OnClickListener{
    private static final String TAG = "LiveActivity";
    private static final int MY_PERMISSIONS_REQUEST_ACCESS_EXTERNAL_STORAGE = 1;
    private Switch mCameraSwitch;
    private Switch mStreamingSwitch;
    private Switch mLivePushSwitch;
    private EditText mLivePushEdit;
    private PanoramaView mPanoramaView;
    private Insta360PanoRenderer mRenderer;
    private CameraService mCameraService;
    private boolean mCameraStreamStarted;
    private Object mSurface;
    private int mCameraStreamWidth = 3840;
    private int mCameraStreamHeight = 1920;
    private int mCameraStreamFps = 30;
    private VideoAntiShakeController videoAntiShakeController;
    private Bundle mExtra = new Bundle();

    private Button mPhotoButton;
    private Button mRecordButton;
    private Button mPlayButton;

    private enum OpenState { Idle, Opening, Opened }
    private OpenState mOpenState = OpenState.Idle;

    private enum RecordState { Idle, Recording, Stopping }
    private RecordState mRecordState = RecordState.Idle;

    private enum LivePushingState { Idle, Pushing, Stopping }
    private LivePushingState mLivePushingState = LivePushingState.Idle;

    public static final int IMAGE_PANO = 101;
    public static final int IMAGE_LITTLE_STAR = 102;
    public static final int VIDEO_WIDTH_ANGLE = 103;
    public static final int VIDEO_PANO = 104;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_live);

        EventBus.getDefault().register(this);
        mCameraService = CameraService.instance(this.getApplicationContext());

        mCameraSwitch = (Switch)findViewById(R.id.camera_switch);
        mStreamingSwitch = (Switch)findViewById(R.id.stream_switch);
        mLivePushSwitch = (Switch)findViewById(R.id.live_push_switch);
        mLivePushEdit = (EditText)findViewById(R.id.live_push_addr);
        mPanoramaView = (PanoramaView)findViewById(R.id.surfaceView);

        mPhotoButton = (Button) findViewById(R.id.ap_bt_takephoto);
        mPhotoButton.setOnClickListener(this);
        mRecordButton = (Button) findViewById(R.id.ap_bt_record);
        mRecordButton.setOnClickListener(this);

        mPlayButton = (Button) findViewById(R.id.ap_bt_play);
        mPlayButton.setOnClickListener(this);

        mCameraSwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(CompoundButton compoundButton, boolean isChecked) {
                if(isChecked)
                    onOpenCamera();
                else
                    onCloseCamera();
            }
        });
        mStreamingSwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(CompoundButton compoundButton, boolean isChecked) {
                if(isChecked)
                    onStartCameraStream();
                else
                    onStopCameraStream();
            }
        });
        mLivePushSwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(CompoundButton compoundButton, boolean isChecked) {
                if(isChecked) {
                    String url;
                    if(mLivePushEdit.getText().toString().length() > 0)
                    {
                        url = mLivePushEdit.getText().toString();
                    }
                    else
                    {
                        //just test live
                        url = "rtmp://video-center.alivecdn.com/livecdn/test526?vhost=hls.insta360.com";
                    }
                    onStartLivePush(url);
                }
                else {
                    onStopLivePush();
                }
            }
        });

        updateUI();
        initPanoView();
    }

    @Override
    public void onClick(View v) {
        if (v== mPlayButton)
        {
            Log.d(TAG,"player reaction");
            Intent mIntent = new Intent(LiveActivity.this,PlayActivity.class);
            startActivity(mIntent);
            finish();
        }
        if(!mCameraStreamStarted)
            return;
        if(v == mRecordButton) {
            if(mRecordState == RecordState.Recording) {
                stopRecord();
            } else if(mRecordState == RecordState.Idle) {
                startRecord();
            }
        }
        else if(v == mPhotoButton) {
            String filename = new File(Environment.getExternalStorageDirectory(), "one_demo/photo_" +
                    new SimpleDateFormat("MM_dd_HH_mm_ss_SSS", Locale.getDefault()).format(new Date()) + ".jpg").getAbsolutePath();
            testCaptureStillImageWithouStorage(filename,false);
        }
    }

    /**
     * capture still origin image without tf card in camera
     * note: please keep no tf card in camera
     * @param mPath
     * @param bStitch not used
     */

    private void testCaptureStillImageWithouStorage(String mPath,boolean bStitch)
    {
        Log.i(TAG, "testCaptureStillImageWithouStorage");
        TakePicture obj = new TakePicture();
        obj.mode = OneDriverInfo.Request.ImageMode.NORMAL;
        obj.extra_metadata = null;
        obj.aeb_ev_bias = new int[3];
        obj.aeb_ev_bias[0] = 0;
        obj.aeb_ev_bias[1] = 1;
        obj.aeb_ev_bias[2] = 2;
        mCameraService.captureStillImageWithouStorage(mPath,bStitch,obj);
    }

    //record original video
    private void startCaptureWithoutStorage()
    {
        mCameraService.stopStreaming();
        mCameraService.setAudioParam(true,48000,128000,OneDriverInfo.AudioCodec.ADTS);
        mCameraService.startStreaming();

        StartCaptureWithoutStorage obj = new StartCaptureWithoutStorage();
        String filename = new File(Environment.getExternalStorageDirectory(), "one_demo/VID" +
                new SimpleDateFormat("MM_dd_HH_mm_ss_SSS", Locale.getDefault()).format(new Date()) + ".insv").getAbsolutePath();
        Log.d(TAG,"org url " + filename);
        obj.path = filename;
        obj.format = OneDriverInfo.RecordFormat.VIDEO_FORMAT_MP4;
        obj.width = 3840;
        obj.height = 1920;
        obj.fps = 30;
        obj.bitrate = 20000000;
        obj.mRecordType = RecordType.Origin;
        obj.renderMode = RenderMode.withGlRenderer(RenderMethod.DualFishEyeStitchingPlanar);
        mCameraService.startRecordWithoutStorage(obj);
    }

    private void initPanoView() {
        mPanoramaView.setFrameRate(60.0);
        mPanoramaView.setRenderMode(PanoramaView.RENDERMODE_WHEN_DIRTY);
        mRenderer = new Insta360PanoRenderer(getApplicationContext());

        IPlayerFactory playerFactory = new IPlayerFactory() {
            @Override
            public ISurfacePlayer makePlayer(PlayerCallback playerCallback) {
                return new TestSurfacePlayer(){
                    @Override
                    public boolean useGLTarget()
                    {
                        return true;
                    }

                    @Override
                    public void onCreateGLTarget(GlTarget glTarget)
                    {
                        android.util.Log.i(TAG, "camera stream, set glTarget: " + glTarget);
                        mSurface = glTarget;
                        mCameraService.setSurface(glTarget);
                    }

                    @Override
                    public void onReleaseGLTarget(GlTarget glTarget)
                    {
                        if(mCameraService.getSurface() == glTarget)
                            mCameraService.setSurface(null);
                        mSurface = null;
                    }

                    @Override
                    public void onCreateSurface(Surface surface) {
                        android.util.Log.i(TAG, "camera stream, set surface: " + surface);
                        mSurface = surface;
                        mCameraService.setSurface(surface);
                    }

                    @Override
                    public void onReleaseSurface(Surface surface) {
                        mCameraService.setSurface(null);
                        mSurface = null;
                    }

                    @Override
                    public boolean isPlaying() {
                        return mCameraStreamStarted;
                    }

                    @Override
                    public boolean readExtras()
                    {
                        return false;
                    }

                    @Override
                    public Bundle getExtra()
                    {
                        return mExtra;
                    }

                    @Override
                    public float getVolume() {
                        return 0;
                    }

                    @Override
                    public String getGyro() {
                        return null;
                    }
                };
            }
        };
        mRenderer.initWithReducedMode(new FishEyeStrategy(),
                playerFactory, new SphericalStitchModel(mRenderer.getId()));
        mPanoramaView.setRenderer(mRenderer);

        //手势
        GestureController controller = new GestureController(getApplicationContext(), mRenderer.getRenderModel().getCamera());
        controller.setHolders(mRenderer.getRenderModel().getLayerAt(0));
        controller.setEnabled(true);
        mRenderer.getControllerManager().addController(GestureController.class.getSimpleName(), controller);
        //防抖
        videoAntiShakeController = new VideoAntiShakeController(new GyroMatrixProvider() {
            @Override
            public float[] getMatrix(double v) {
//                Log.w(TAG, "use deprecated api: getCurrentGyroMatrix");
                return mCameraService.getCurrentGyroMatrix();
            }

            @Override
            public GyroMatrixType getGyroMatrixType() {
                return GyroMatrixType.ONE;
            }
        });

        videoAntiShakeController.setHolders(mRenderer.getRenderModel().getLayerAt(1));
        videoAntiShakeController.setEnabled(true);
        mRenderer.getControllerManager().addController(VideoAntiShakeController.class.getSimpleName(), videoAntiShakeController);

//        setAntishakeEnabled(true, mRenderer, videoAntiShakeController, "usb://.insv");

        mRenderer.getSourceManager().setOnLoadSourceListener(new OnLoadSourceListener() {
            @Override
            public void loadSourceFinish(ISource iSource) {
                //底部logo
                setLogo(R.drawable.setting_ic_logowhite_zh);
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        Toast.makeText(LiveActivity.this , "loadSourceFinish" , 0).show();
                    }
                });
            }

            @Override
            public void loadSourceError(SourceException e) {
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        Toast.makeText(LiveActivity.this , "loadSourceError" , 0).show();
                    }
                });
            }
        });

//        mRenderer.getTextureHolder().getPlayerDelegate().setOnPreparedListener(new ISurfacePlayer.OnPreparedListener() {
//            @Override
//            public void onPrepared(ISurfacePlayer iSurfacePlayer) {
//                iSurfacePlayer.start();
//            }
//        });
        mRenderer.onPlayerPrepareOK();
    }

    public static void setAntishakeEnabled(final boolean enable, final Insta360PanoRenderer renderer, final VideoAntiShakeController videoAntiShakeController, String url) {
        String lowerCaseUrl = url.toLowerCase();
        if (lowerCaseUrl.endsWith(".mp4") || lowerCaseUrl.endsWith(".insv")) {
            renderer.internalOfferTask(new Runnable() {
                @Override
                public void run() {
                    renderer.getRenderModel().buildModel(renderer, renderer.getSourceManager().getCurrentSource());
                    if (renderer.getRenderModel() instanceof SphericalRenderModel) {
                        android.util.Log.i("Insta360", "use vert transfer mode");
                        videoAntiShakeController.setHolders(renderer.getRenderModel().getLayerAt(1));
                    } else if (renderer.getRenderModel() instanceof PlanarRenderModel) {
                        videoAntiShakeController.setHolders(renderer.getRenderModel());
                    }
                    videoAntiShakeController.setEnabled(enable);
                    renderer.getControllerManager().addController(VideoAntiShakeController.class.getSimpleName(), videoAntiShakeController);
                    if (!enable) {
                        renderer.getRenderModel().getLayerAt(1).setOrientation(new Quaternion());
                    }
                }
            });
        } else {
            renderer.internalOfferTask(new Runnable() {
                @Override
                public void run() {
                    renderer.getControllerManager().removeController(VideoAntiShakeController.class.getSimpleName());
                    renderer.getRenderModel().getLayerAt(1).setOrientation(new Quaternion());
                }
            });
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        //destroy
        if(mRenderer != null) {
            mRenderer.onDestroy();
            mRenderer = null;
        }
    }

    private void onOpenCamera() {
        mCameraService.open(null);
        mOpenState = OpenState.Opening;
        updateUI();
    }

    private void onCloseCamera() {
        if(mLivePushingState != LivePushingState.Idle)
        {
            mCameraService.stopRecord(null);
        }
        mCameraService.close();
        mOpenState = OpenState.Idle;
        mCameraStreamStarted = false;
        mLivePushingState = LivePushingState.Idle;
        updateUI();
    }

    private void onStartCameraStream() {
        // 3840x1920@30fps H.264
        mCameraService.setVideoParam(mCameraStreamWidth, mCameraStreamHeight, mCameraStreamFps,
                40, true);
        mCameraService.setAudioSource(AudioSource.AUDIO_CAMERA);
        mCameraService.fixSomething();
        mCameraService.switchRenderMode(RenderMode.withGlRenderer(RenderMethod.PlanarKeep));
        mCameraService.startStreaming();

        ISource source = SourceFactory.create("usb://.mp4");
        source.updateOffset(getOffset());
//            setAntishakeEnabled(true, mRenderer, videoAntiShakeController, "usb://.insv");
        mRenderer.getSourceManager().start(source);

        mCameraStreamStarted = true;
        updateUI();
    }

    private void onStopCameraStream() {
        if(!mCameraStreamStarted)
            return;
        mCameraService.stopStreaming();
        mCameraStreamStarted = false;
        updateUI();
        removeLogo();
    }

    /**
     * if url starts with "rtmp" or "rtsp",it react live,
     * if url not starts with "rtmp" or "rtsp",it react record pano video
     * @param url live url or recorded pano vidoe file path
     */
    private void onStartLivePush(String url) {
        // set the output resolution, fps, bitrate and so on of the encoder
        StartCaptureWithoutStorage obj = new StartCaptureWithoutStorage();
        double fps = 30;
        double bitrateMbps = 40;
        int width = 1440;
        int height = 720;

        obj.path = url;
        Log.d(TAG,"live url " + url);
            //live
        obj.format = OneDriverInfo.RecordFormat.VIDEO_FORMAT_FLV;

        obj.width = width;
        obj.height = height;
        obj.bitrate = (int)(bitrateMbps * 1024 * 1024);
        obj.fps = (int)fps;
        obj.renderMode = RenderMode.withGlRenderer(RenderMethod.DualFishEyeStitchingPlanar);
        obj.mRecordType = RecordType.ReEncoded;

        mCameraService.stopStreaming();
        mCameraService.setAudioParam(true,48000,128000,OneDriverInfo.AudioCodec.ADTS);
        mCameraService.switchRenderMode(RenderMode.withGlRenderer(RenderMethod.PlanarKeep));
        mCameraService.startStreaming();

        mCameraService.startRecordWithoutStorage(obj);
        mLivePushingState = LivePushingState.Pushing;
        updateUI();
    }

    private void onStopLivePush() {
        if(mLivePushingState != LivePushingState.Pushing)
            return;
        // this is a async request, will receive notification when stop complete
        mCameraService.stopRecord(null);
        mLivePushingState = LivePushingState.Stopping;
        updateUI();
    }

    private void updateUI() {
        if(mCameraStreamStarted)
        {
            mPlayButton.setEnabled(false);
            mStreamingSwitch.setEnabled(true);
            mStreamingSwitch.setChecked(true);

            mPhotoButton.setEnabled(true);

            setSurfaceViewSize(mCameraStreamWidth, mCameraStreamHeight);

            if(mLivePushingState == LivePushingState.Stopping) {
                mLivePushSwitch.setEnabled(false);
                mRecordButton.setEnabled(false);
            }
            else if(mLivePushingState == LivePushingState.Idle){
                if(mRecordState == RecordState.Stopping) {
                    mRecordButton.setEnabled(false);
                    mLivePushSwitch.setEnabled(false);
                }
                else if(mRecordState == mRecordState.Idle){
                    mRecordButton.setEnabled(true);
                    mLivePushSwitch.setEnabled(true);
                    mLivePushSwitch.setChecked(false);
                }
                else {
                    mLivePushSwitch.setEnabled(false);
                    mRecordButton.setEnabled(true);
                }
            }
            else {
                mRecordButton.setEnabled(false);
                mLivePushSwitch.setEnabled(true);
                mLivePushSwitch.setChecked(true);
            }

            if(mLivePushingState == LivePushingState.Idle && mRecordState == RecordState.Idle)
            {
                mPhotoButton.setEnabled(true);
            }
            else
            {
                mPhotoButton.setEnabled(false);
            }
        }
        else
        {
            mPhotoButton.setEnabled(false);
            mRecordButton.setEnabled(false);
            mLivePushSwitch.setEnabled(false);

            if(mOpenState != OpenState.Opened) {
                mStreamingSwitch.setEnabled(false);
                mStreamingSwitch.setChecked(false);
                mPlayButton.setEnabled(true);
            }
            else {
                mStreamingSwitch.setEnabled(true);
                mStreamingSwitch.setChecked(false);
                mPlayButton.setEnabled(false);
            }
        }


    }

    /**
     * record pano video
     */
    private void startRecord() {
        String url = new File(Environment.getExternalStorageDirectory(), "one_demo/VID" +
                new SimpleDateFormat("MM_dd_HH_mm_ss_SSS", Locale.getDefault()).format(new Date()) + ".insv").getAbsolutePath();

        StartCaptureWithoutStorage obj = new StartCaptureWithoutStorage();
        double fps = 30;
        double bitrateMbps = 40;
        int width = 1440;
        int height = 720;

        obj.path = url;
        Log.d(TAG,"record pano url " + url);
            //record with pano video
        obj.format = OneDriverInfo.RecordFormat.VIDEO_FORMAT_MP4;

        obj.width = width;
        obj.height = height;
        obj.bitrate = (int)(bitrateMbps * 1024 * 1024);
        obj.fps = (int)fps;
        obj.renderMode = RenderMode.withGlRenderer(RenderMethod.DualFishEyeStitchingPlanar);
        obj.mRecordType = RecordType.ReEncoded;

        mCameraService.stopStreaming();
        mCameraService.setAudioParam(true,48000,128000,OneDriverInfo.AudioCodec.ADTS);
        mCameraService.switchRenderMode(RenderMode.withGlRenderer(RenderMethod.PlanarKeep));
        mCameraService.startStreaming();

        mCameraService.startRecordWithoutStorage(obj);

        mRecordButton.setText(R.string.str_record_stop);
        mLivePushEdit.setEnabled(false);
        mRecordState = RecordState.Recording;
    }

    private void stopRecord() {
        Log.i(TAG, "stop recording...");

        byte[] extra = getExtraMetaData();

        Log.d(TAG,"extra len " + extra.length);

        mCameraService.stopRecord(extra);
        mRecordState = RecordState.Stopping;

        mRecordButton.setText(R.string.str_record_stopping);
        mRecordButton.setEnabled(false);

        mPlayButton.setEnabled(false);
    }

    private String readCameraOffset() {
        List<String> mList = new ArrayList<>();
        mList.add(OneDriverInfo.Options.MEDIA_OFFSET);
        mList.add(OneDriverInfo.Options.ORIGIN_OFFSET);
        Options mOptions = mCameraService.getOptions(mList);
        String offset = mOptions.getMediaOffset();

        Log.d(TAG,"offset " + offset);

        if(offset == null)
        {
            offset = mOptions.getOriginOffset();
            Log.d(TAG,"2offset " + offset);
        }

        return offset;
    }


    private byte[] getExtraMetaData() {
        ARObject arObject = ARObject.create(null);
        arObject.setOffset(readCameraOffset());

        arObject.setCameraType("Insta360 ONE");
        arObject.setCreationTime(System.currentTimeMillis());
        ExtraMetadata extraMetadata = ARObject.bundleToExtraMetadata(arObject);

        return ExtraMetadata.ADAPTER.encode(extraMetadata);
    }

    private void resetRecord() {
        Log.i(TAG, "reset record");
        mCameraService.resetRecord();
        mRecordState = RecordState.Idle;
        mLivePushingState = LivePushingState.Idle;

        mRecordButton.setEnabled(true);
        mLivePushEdit.setEnabled(true);
        updateUI();
    }

    private void setSurfaceViewSize(int videoWidth, int videoHeight) {
        int w = mPanoramaView.getWidth();
        int h = mPanoramaView.getHeight();
        if(w * videoHeight > h * videoWidth)
            w = h * videoWidth / videoHeight;
        else
            h = w * videoHeight / videoWidth;
        android.view.ViewGroup.LayoutParams lp = mPanoramaView.getLayoutParams();
        lp.width = w;
        lp.height = h;
        mPanoramaView.setLayoutParams(lp);
    }

    private void requestPermission() {
        if(ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE)
                != PackageManager.PERMISSION_GRANTED ||
                ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)
                        != PackageManager.PERMISSION_GRANTED ||
                ContextCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO)
                        != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(this,
                    new String[]{
                            Manifest.permission.READ_EXTERNAL_STORAGE,
                            Manifest.permission.WRITE_EXTERNAL_STORAGE,
                            Manifest.permission.RECORD_AUDIO,
                    },
                    MY_PERMISSIONS_REQUEST_ACCESS_EXTERNAL_STORAGE);
        }
    }

    private static final String LOGO_NAME = "logo";
    private void setLogo(int resId) {
        ISticker sticker;
        if (mRenderer.getRenderModel() instanceof SphericalRenderModel) {
            sticker = StickerFactory.createSphericalSticker(this, mRenderer.getId(), resId,
                    32.5,   //size
                    Vector3.X,  //position
                    0,  //rotation
                    1.0f,   //alpha
                    true);
        } else {
            sticker = StickerFactory.createPlanarSticker(this, mRenderer.getId(), resId, 32.5, 0, 1.0f, true);
        }
        removeLogo();
        mRenderer.getRenderModel().getStickerManager().addSticker(LOGO_NAME, sticker);
    }

    private void setLogo(String id) {
        ISticker sticker;
        if (mRenderer.getRenderModel() instanceof SphericalRenderModel) {
            sticker = StickerFactory.createSphericalSticker(this, mRenderer.getId(), id, 32.5, Vector3.NEG_Y, 0, 1.0f, true);
        } else {
            sticker = StickerFactory.createPlanarSticker(this, mRenderer.getId(), id, 32.5, 0, 1.0f, true);
        }
        removeLogo();
        mRenderer.getRenderModel().getStickerManager().addSticker(LOGO_NAME, sticker);
    }


    public void removeLogo() {
        mRenderer.getRenderModel().getStickerManager().removeSticker(LOGO_NAME);
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        if(requestCode != MY_PERMISSIONS_REQUEST_ACCESS_EXTERNAL_STORAGE)
            super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        boolean permissionNotGrant = false;
        if(permissions.length == 0)
            permissionNotGrant = true;
        else {
            for(int i = 0; i < permissions.length; ++i) {
                if(grantResults[i] != PackageManager.PERMISSION_GRANTED) {
                    permissionNotGrant = true;
                    break;
                }
            }
        }
        if(permissionNotGrant) {
            new AlertDialog.Builder(this)
                    .setCancelable(false)
                    .setTitle(R.string.permission_lost_dialog_title)
                    .setMessage(R.string.permission_lost_dialog_message)
                    .setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() {
                        public void onClick(DialogInterface dialog, int which) {
                            requestPermission();
                        }
                    })
                    .setIcon(android.R.drawable.ic_dialog_alert)
                    .show();
        }
    }

    @Override
    protected void onStart() {
        super.onStart();
        requestPermission();
    }

    private void showCameraError(String message) {
        new AlertDialog.Builder(this)
                .setTitle(R.string.camera_error)
                .setMessage(message)
                .setCancelable(false)
                .setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialogInterface, int i) {
                        mCameraSwitch.setChecked(false);
                    }
                })
                .setIcon(android.R.drawable.ic_dialog_alert)
                .create().show();
    }

    private void showRecordError(int err) {
        new AlertDialog.Builder(this)
                .setMessage(R.string.record_error_message)
                .setCancelable(false)
                .setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialogInterface, int i) {
                        resetRecord();
                    }
                })
                .setIcon(android.R.drawable.ic_dialog_alert)
                .create().show();
    }

    private String getOffset()
    {
        List<String> mKeys = new ArrayList<>();
        mKeys.add(OneDriverInfo.Options.MEDIA_OFFSET);

        Options mOptions = mCameraService.getOptions(mKeys);

        String offset = mOptions.getMediaOffset();
//        Log.d(TAG,"offset " + offset);

        return offset;
    }

    @Subscribe(threadMode = ThreadMode.MAIN)
    public void onOpenEvent(CameraService.OpenEvent event) {
        if(mOpenState != OpenState.Opening)
            return;
        mOpenState = OpenState.Opened;
        String offset = getOffset();
        mCameraService.updatePanoOffset(offset);
        updateUI();
    }

    @Subscribe(threadMode = ThreadMode.MAIN)
    public void onDetachEvent(CameraService.DetachEvent event) {
        Log.i(TAG, "camera has detached");
        new AlertDialog.Builder(this).
                setTitle(R.string.detach_note).
                setMessage(R.string.camera_detached_message).
                setCancelable(false).
                setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialogInterface, int i) {
                        mCameraSwitch.setChecked(false);
                    }
                }).
                setIcon(android.R.drawable.ic_dialog_alert).create().show();
    }

    @Subscribe(threadMode = ThreadMode.MAIN)
    public void onErrorEvent(CameraService.ErrorEvent event) {
        if(mOpenState == OpenState.Idle)
            return;
        Log.i(TAG, "camera met error: " + event.error);
        if(event.error == Error.ERR_NOCAMERA)
            showCameraError(getString(R.string.camera_not_attached));
        else
            showCameraError("Camera met error: " + event.error);
    }

    // record or live push error
    @Subscribe(threadMode = ThreadMode.MAIN)
    public void onRecordErrorEvent(CameraService.RecordErrorEvent event) {
        Log.i(TAG, "live error");
        if(mLivePushingState == LivePushingState.Idle)
            return;
        showRecordError(event.error);
    }

    @Subscribe(threadMode = ThreadMode.MAIN)
    public void onRecordFpsUpdateEvent(CameraService.RecordFpsUpdateEvent event) {
        Log.i(TAG, "fps update: " + event.fps);
    }

    @Subscribe(threadMode = ThreadMode.MAIN)
    public void onRecordCompleteEvent(CameraService.RecordCompleteEvent event) {
        Log.i(TAG, "record or live complete");
        mRecordState = RecordState.Idle;
        mRecordButton.setText(R.string.str_record);
        mLivePushingState = LivePushingState.Idle;
        updateUI();
    }

    @Subscribe(threadMode = ThreadMode.MAIN)
    public void onStillImageCapturedEvent(CameraService.StillImageCapturedEvent event) {
        Log.i(TAG, "capture complete");
        if(event.error == 0)
        {
            Toast.makeText(LiveActivity.this, "take picture success",
                    Toast.LENGTH_LONG).show();
        }
        else
        {
            Toast.makeText(LiveActivity.this, "take picture fail",
                    Toast.LENGTH_LONG).show();
        }
    }
}